
#include "ReverseProxyServer.h"
#include "CryptoUtil.h"
#include "signal.h"
#include <wfrest/HttpServer.h>
#include <nlohmann/json.hpp>
#include <string>
#include <map>

using namespace wfrest;
using namespace protocol;
using JSON = nlohmann::json;
using std::string;
using std::map;

ReverseProxyServer::ReverseProxyServer()
:m_service()
{};

void ReverseProxyServer::register_modules()
{
    // 设置静态资源的路由
    register_static_resources_module();
    register_signup_module();
    register_signin_module();
    register_userinfo_module(); 
    register_filelist_module(); 
    register_fileupload_module();
    register_filedownload_module(); 
}


void ReverseProxyServer::register_static_resources_module()
{
    m_server.GET("/user/signup", [](const HttpReq *, HttpResp * resp){
        resp->File("static/view/signup.html");
    });

    m_server.GET("/static/view/signin.html", [](const HttpReq *, HttpResp * resp){
                 resp->File("static/view/signin.html");
                 });

    m_server.GET("/static/view/home.html", [](const HttpReq *, HttpResp * resp){
                 resp->File("static/view/home.html");
                 });

    m_server.GET("/static/js/auth.js", [](const HttpReq *, HttpResp * resp){
                 resp->File("static/js/auth.js");
                 });

    m_server.GET("/static/img/avatar.jpeg", [](const HttpReq *, HttpResp * resp){
                 resp->File("static/img/avatar.jpeg");
                 });

    m_server.GET("/file/upload", [](const HttpReq *, HttpResp * resp){
                 resp->File("static/view/index.html");
                 });

    m_server.Static("/file/upload_files","static/view/upload_files");
}


void ReverseProxyServer::register_signin_module()
{
    m_server.POST("/user/signin",[this](const HttpReq* req, HttpResp* resp,SeriesWork* series){
                  string username;
                  string password;
                  const map<string, string> &  prams = req->form_kv();
                  for(const auto & [key,value]:prams){
                  if(key == "username"){
                  username = value;
                  }
                  if(key == "password"){
                  password = value;
                  }
                  }

                  string ip;
                  unsigned short port;
                  m_service.getService(ip,port,"signService");
                 
                  #ifdef DEBUG
                    std::cout<<ip<<"\n";
                    std::cout<<port<<"\n";
                  #endif

                  if(!m_service.success()){
                       resp->String("error");
                  }

                  Sign::SRPCClient signClient(ip.c_str(),port);
                  
                  SignRequest signin_req;
                  signin_req.set_username(username);
                  signin_req.set_password(password);

                  // 2. 调用远程方法 (和调用本地方法的形式是一样的)
                  SignResponse signin_resp; 
                  RPCSyncContext ctx;
                  signClient.signIn(&signin_req, &signin_resp, &ctx); /* 同步调用 */

                  if (ctx.success) {
                      if(signin_resp.success()){
                          JSON js;
                          js["data"] = {{"Token",signin_resp.token()},
                              {"Username",signin_resp.username()},
                              {"Location","/static/view/home.html"}};
                          resp->String(js.dump());
                      }else{
                          resp->String("error");
                      }
                  } else {
                      std::cerr << "status code: " << ctx.status_code 
                          << ", error: " << ctx.error
                          << ", errmsg: " << ctx.errmsg << "\n";
                      resp->String("Rpc_error");
                  }
    });
}


void ReverseProxyServer::register_signup_module()
{
    m_server.POST("/user/signup",[this](const HttpReq* req, HttpResp* resp,SeriesWork* series){
        string username;
        string password;
        const map<string, string> &  prams = req->form_kv();
        for(const auto & [key,value]:prams){
            if(key == "username"){
                username = value;
            }
            if(key == "password"){
                password = value;
            }
        }
         #ifdef DEBUG
            std::cout<<username<<"\n";
            std::cout<<password<<"\n";
        #endif

        string ip;
        unsigned short port;
        m_service.getService(ip,port,"signService");
        #ifdef DEBUG
        std::cout<<ip<<"\n";
        std::cout<<port<<"\n";
        #endif
        if(!m_service.success()){
            resp->String("error");
        }
        Sign::SRPCClient signClient(ip.c_str(),port);

        SignRequest signup_req;
        signup_req.set_username(username);
        signup_req.set_password(password);

                  // 2. 调用远程方法 (和调用本地方法的形式是一样的)
        SignResponse signup_resp; 
        RPCSyncContext ctx;
        signClient.signUp(&signup_req, &signup_resp, &ctx);
    
        if (ctx.success) {
                      if(signup_resp.success()){
                          resp->String("SUCCESS");
                      }else{
                          resp->String("error");
                      }
        } else {
                      std::cerr << "status code: " << ctx.status_code 
                          << ", error: " << ctx.error
                          << ", errmsg: " << ctx.errmsg << "\n";
                      resp->String("Rpc_error");
        }
    });
}


void ReverseProxyServer::register_userinfo_module()
{
    m_server.GET("/user/info", [this](const HttpReq *req, HttpResp *resp,SeriesWork * series){
        string username;
        string token;
        username = req->query("username");
        token = req->query("token");
        if(CryptoUtil::verify_token(token,username)){
            
            string ip;
            unsigned short port;
            m_service.getService(ip,port,"usrInfoService");
            #ifdef DEBUG
            std::cout<<ip<<"\n";
            std::cout<<port<<"\n";
            #endif
            if(!m_service.success()){
                resp->String("error");
            }
            UsrInfo::SRPCClient usrInfoClient(ip.c_str(),port);

            UsrInfoRequest usrinfo_req;
	        usrinfo_req.set_username(username);
            
            UsrInfoResponse usrinfo_resp; 
            RPCSyncContext ctx;
            usrInfoClient.usrInfo(&usrinfo_req, &usrinfo_resp, &ctx);
            
            if (ctx.success) {
                      if(usrinfo_resp.success()){
                          JSON js;
                            js["data"] = {{"Username",usrinfo_resp.username()},
                                         {"SignupAt",usrinfo_resp.signupat()}};
                            resp->String(js.dump());
                      }else{
                          resp->String("error");
                      }
            } else {
                      std::cerr << "status code: " << ctx.status_code 
                          << ", error: " << ctx.error
                          << ", errmsg: " << ctx.errmsg << "\n";
                      resp->String("Rpc_error");
             }

        }else{
            resp->String("error");
        }
    
    });
}


void ReverseProxyServer::register_filelist_module()
{
    m_server.POST("/file/query",[this](const HttpReq* req, HttpResp* resp,SeriesWork* series){
        string username;
        string token;
        username = req->query("username");
        token = req->query("token");
        #ifdef DEBUG
            std::cout<<username<<"\n";
            std::cout<<token<<"\n";
        #endif

        if(CryptoUtil::verify_token(token,username)){
            
            string ip;
            unsigned short port;
            m_service.getService(ip,port,"fileListService");
            #ifdef DEBUG
            std::cout<<ip<<"\n";
            std::cout<<port<<"\n";
            #endif
            if(!m_service.success()){
                resp->String("error");
            }
            FileList::SRPCClient fileListClient(ip.c_str(),port);

            FileListRequest fileList_req;
	        fileList_req.set_username(username);
            
            FileListResponse fileList_resp; 
            RPCSyncContext ctx;
            

            JSON retJs;
            JSON assJs;
            fileListClient.fileList(&fileList_req, &fileList_resp, &ctx);

            if (ctx.success) {
                    if(fileList_resp.success()&&fileList_resp.has()){
                        for(int i = 0 ;i < fileList_resp.filehash_size() ; ++i){
                            assJs["FileHash"] = fileList_resp.filehash(i);
                            assJs["FileName"] = fileList_resp.filename(i);
                            assJs["FileSize"] = fileList_resp.filesize(i);
                            assJs["UploadAt"] = fileList_resp.uploadat(i);
                            assJs["LastUpdated"] = fileList_resp.lastupdated(i);
                            retJs.push_back(assJs);  
                        }
                        resp->String(retJs.dump());
                    }else if(fileList_resp.success()&&!fileList_resp.has()){
                        resp->String("");
                    }else{
                        resp->String("error");
                    }
            } else {
                    std::cerr << "status code: " << ctx.status_code 
                        << ", error: " << ctx.error
                        << ", errmsg: " << ctx.errmsg << "\n";
                    resp->String("Rpc_error");
            }

        }else{
            resp->String("error");
        }
    });   
}


void ReverseProxyServer::register_fileupload_module()
{
    m_server.POST("/file/upload",[this](const HttpReq* req, HttpResp* resp,SeriesWork* series){
        string username;
        string token;
        username = req->query("username");
        token = req->query("token");
        if(CryptoUtil::verify_token(token,username)){ 

            if (req->content_type() != MULTIPART_FORM_DATA) {
                resp->set_status(HttpStatusBadRequest);     
                return ;
            }

            string ip;
            unsigned short port;
            m_service.getService(ip,port,"upLoadService");
            #ifdef DEBUG
            std::cout<<ip<<"\n";
            std::cout<<port<<"\n";
            #endif
            if(!m_service.success()){
                resp->String("error");
            }
            UpLoad::SRPCClient upLoadClient(ip.c_str(),port);

            const Form& form = req->form();
        
            for (const auto& [_, file]: form) {

                auto& [filename, filecontent] = file;
                UpLoadRequest upload_req;
                upload_req.set_username(username);
                upload_req.set_filename(filename);
                upload_req.set_content(filecontent);
                UpLoadResponse upload_resp; 
                RPCSyncContext ctx;

                upLoadClient.upLoad(&upload_req, &upload_resp, &ctx);
                
            if (ctx.success) {
                    if(upload_resp.success()){
                        resp->Redirect("/static/view/home.html", HttpStatusSeeOther);
                    }else{
                        resp->String("error");
                    }
            } else {
                      std::cerr << "status code: " << ctx.status_code 
                          << ", error: " << ctx.error
                          << ", errmsg: " << ctx.errmsg << "\n";
                      resp->String("Rpc_error");
             }

            }
        }else{

            resp->String("error");
        
        }
    });
}


void ReverseProxyServer::register_filedownload_module()
{
    m_server.GET("/file/download",[this](const HttpReq* req, HttpResp* resp,SeriesWork* series){
        string username;
        string token;
        string filename;
        string filehash;
        username = req->query("username");
        token = req->query("token");
        filename = req->query("filename");
        filehash = req->query("filehash");
        
        string filePath = "../file/"+filehash;

        if(CryptoUtil::verify_token(token,username)){

            string ip;
            unsigned short port;
            m_service.getService(ip,port,"downLoadService");
            #ifdef DEBUG
            std::cout<<ip<<"\n";
            std::cout<<port<<"\n";
            #endif
            if(!m_service.success()){
                resp->String("error");
            }

            DownLoad::SRPCClient downLoadClient(ip.c_str(),port);
            
            DownLoadRequest download_req;
            download_req.set_filepath(filePath);
    
                  // 2. 调用远程方法 (和调用本地方法的形式是一样的)
        DownLoadResponse download_resp; 
        RPCSyncContext ctx;
        downLoadClient.downLoad(&download_req, &download_resp, &ctx);
    
        if (ctx.success) {
                      if(download_resp.success()){
                          resp->String(download_resp.content());
                      }else{
                           resp->set_status_code(std::to_string(download_resp.code()));
                           resp->append_output_body(download_resp.content());
                      }
        } else {
                      std::cerr << "status code: " << ctx.status_code 
                          << ", error: " << ctx.error
                          << ", errmsg: " << ctx.errmsg << "\n";
                      resp->String("Rpc_error");
        }
        }

    });
}


WFFacilities::WaitGroup g_waitGroup { 1 };

void sig_handler(int)
{
    g_waitGroup.done();
}

int main()
{
    signal(SIGINT, sig_handler);    

    ReverseProxyServer server {};

    // 注册路由
    server.register_modules();
    
    if (server.track().start(8888) == 0) {
        server.list_routes();
        g_waitGroup.wait();
        server.stop();
    } else {
        std::cerr << "Error: Server start failed!\n";
    }
}
